package com.rt.gptnext.common.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.codec.Base62;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.id.NanoId;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.rt.gptnext.common.constant.Constants;
import com.rt.gptnext.common.constant.HttpConstants;
import com.rt.gptnext.common.utils.json.JsonUtil;
import org.slf4j.MDC;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 常用工具类
 *
 * @author gaoyiyang
 **/
public class CommonUtil {

	private final static char[] ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();

	/**
	 * 拷贝bean
	 *
	 * @param src              原始对象
	 * @param targetClass      目标类型
	 * @param ignoreProperties 忽略字段
	 * @param <S>              原始类型
	 * @param <T>              转换后类型
	 * @return 拷贝结果
	 */
	public static <S, T> T copyBean(S src, Class<T> targetClass, String... ignoreProperties) {
		T target = ReflectUtil.newInstanceIfPossible(targetClass);
		return copyBean(src, target, ignoreProperties);
	}

	/**
	 * 拷贝bean
	 *
	 * @param src              原始对象
	 * @param target           目标对象
	 * @param ignoreProperties 忽略字段
	 * @param <S>              原始类型
	 * @param <T>              转换后类型
	 * @return 拷贝结果
	 */
	public static <S, T> T copyBean(S src, T target, String... ignoreProperties) {
		return copyBean(src, target, true, ignoreProperties);
	}

	/**
	 * 拷贝bean
	 *
	 * @param src              原始对象
	 * @param target           目标对象
	 * @param ignoreNullValue  是否忽略空值
	 * @param ignoreProperties 忽略字段
	 * @param <S>              原始类型
	 * @param <T>              转换后类型
	 * @return 拷贝结果
	 */
	public static <S, T> T copyBean(S src, T target, boolean ignoreNullValue, String... ignoreProperties) {
		BeanUtil.copyProperties(src, target, CopyOptions
			.create()
			.setIgnoreNullValue(ignoreNullValue)
			.setIgnoreProperties(ignoreProperties));
		return target;
	}

	/**
	 * 拷贝列表
	 *
	 * @param src              原始集合
	 * @param targetClass      目标类型
	 * @param ignoreProperties 忽略字段
	 * @param <S>              原始类型
	 * @param <T>              转换后类型
	 * @return 拷贝结果
	 */
	public static <S, T> List<T> copyList(List<S> src, Class<T> targetClass, String... ignoreProperties) {
		if (src == null) {
			return null;
		}
		if (src.isEmpty()) {
			return new ArrayList<>();
		}
		return src.stream()
			.map(s -> copyBean(s, targetClass, ignoreProperties))
			.collect(Collectors.toList());
	}

	/**
	 * 转换对象为json字符串
	 *
	 * @param obj 对象
	 * @return json字符串
	 */
	public static String toJsonStr(Object obj) {
		return JsonUtil.toJsonString(obj);
	}

	/**
	 * 获取对象值
	 *
	 * @param obj          对象
	 * @param expression   取值表达式
	 * @param defaultValue 默认值
	 * @return 获取到的值
	 */
	public static String getValue(Object obj, String expression, String defaultValue) {
		return getValue(obj, expression, String.class, defaultValue);
	}

	/**
	 * 获取对象值
	 *
	 * @param obj        对象
	 * @param expression 取值表达式
	 * @return 获取到的值
	 */
	public static String getValue(Object obj, String expression) {
		return getValue(obj, expression, String.class);
	}

	/**
	 * 获取对象值
	 *
	 * @param obj          对象
	 * @param expression   取值表达式
	 * @param targetClass  值类型
	 * @param <T>          对象值类型
	 * @param defaultValue 默认值
	 * @return 获取到的值
	 */
	public static <T> T getValue(Object obj, String expression, Class<T> targetClass, T defaultValue) {
		return getValue(obj, expression, targetClass, (Supplier<T>) () -> defaultValue);
	}

	/**
	 * 获取对象值
	 *
	 * @param obj         对象
	 * @param expression  取值表达式
	 * @param targetClass 值类型
	 * @param <T>         对象值类型
	 * @return 获取到的值
	 */
	public static <T> T getValue(Object obj, String expression, Class<T> targetClass) {
		return getValue(obj, expression, targetClass, (T) null);
	}

	/**
	 * 获取对象值
	 *
	 * @param obj                  对象
	 * @param expression           取值表达式
	 * @param targetClass          值类型
	 * @param defaultValueSupplier 默认值提供器
	 * @param <T>                  对象值类型
	 * @return 获取到的值
	 */
	public static <T> T getValue(Object obj, String expression, Class<T> targetClass, Supplier<T> defaultValueSupplier) {
		if (defaultValueSupplier == null) {
			defaultValueSupplier = () -> null;
		}
		try {
			if (obj instanceof Map) {
				T t = MapUtil.get((Map<?, ?>) obj, expression, targetClass);
				if (t != null && !StrUtil.isBlankIfStr(t)) {
					return t;
				}
			}
			Object property = BeanUtil.getProperty(obj, expression);
			if (property == null) {
				return defaultValueSupplier.get();
			}
			if (targetClass.isAssignableFrom(property.getClass())) {
				return (T) property;
			}
			T value = Convert.convert(targetClass, property, null);
			if (value != null) {
				return value;
			}
		} catch (Exception ignore) {

		}
		return defaultValueSupplier.get();
	}

	/**
	 * list去重
	 *
	 * @param list 待去重list
	 */
	public static <T> void listDistinct(List<T> list) {
		List<T> result = CollUtil.distinct(list);
		list.clear();
		list.addAll(result);
	}


	/**
	 * list去重
	 *
	 * @param list     待去重list
	 * @param function 获取去重键的函数
	 */
	public static <T, K> void listDistinct(List<T> list, Function<T, K> function) {
		List<T> result = CollUtil.distinct(list, function, true);
		list.clear();
		list.addAll(result);
	}

	/**
	 * 对象o是否在数组中存在
	 *
	 * @param o   比较对象
	 * @param arr 数组
	 * @return 是否存在
	 */
	@SafeVarargs
	public static <T> boolean contains(T o, T... arr) {
		return ArrayUtil.contains(arr, o);
	}

	/**
	 * 根据时间生成唯一ID
	 *
	 * @return -
	 */
	public static String timeId() {
		return DateUtil.format(new Date(SystemClock.now()), "YYMMddHHmmss") + Constants.UNDERLINE + NanoId.randomNanoId(null, ALPHABET, 6);
	}

	/**
	 * 获取nanoId
	 *
	 * @param size 长度
	 * @return id
	 */
	public static String nanoId(int size) {
		return NanoId.randomNanoId(null, ALPHABET, size);
	}

	/**
	 * 获取文件的MIME类型
	 *
	 * @param filePath 文件地址
	 * @return MIME类型
	 */
	public static String getMimeType(String filePath) {
		if (filePath == null) {
			return null;
		}
		if (StrUtil.endWithIgnoreCase(filePath, "docx")) {
			return HttpConstants.MimeTypes.DOCX;
		}
		if (StrUtil.endWithIgnoreCase(filePath, "doc")) {
			return HttpConstants.MimeTypes.DOC;
		}
		if (StrUtil.endWithIgnoreCase(filePath, "pdf")) {
			return HttpConstants.MimeTypes.PDF;
		}
		if (StrUtil.endWithIgnoreCase(filePath, "xls")) {
			return HttpConstants.MimeTypes.XLS;
		}
		if (StrUtil.endWithIgnoreCase(filePath, "xlsx")) {
			return HttpConstants.MimeTypes.XLSX;
		}
		return FileUtil.getMimeType(filePath);
	}

	/**
	 * 字符串转list（按逗号进行拆分，过于复杂的类型用该方法转换会有问题）
	 *
	 * @param str         源字符串
	 * @param targetClass 转换后类型
	 * @return -
	 */
	public static <T> List<T> str2List(String str, Class<T> targetClass) {
		return StrUtil.splitTrim(str, Constants.COMMA)
			.stream()
			.map(s -> Convert.convert(targetClass, s))
			.collect(Collectors.toList());
	}

	/**
	 * 字符串转list（按逗号进行拆分，过于复杂的类型用该方法转换会有问题）
	 *
	 * @param str 源字符串
	 * @return -
	 */
	public static List<String> str2List(String str) {
		return str2List(str, String.class);
	}

	/**
	 * 字符串转Long类型list（按逗号进行拆分，过于复杂的类型用该方法转换会有问题）
	 *
	 * @param str 源字符串
	 * @return -
	 */
	public static List<Long> str2LongList(String str) {
		return str2List(str, Long.class);
	}

	/**
	 * bean是否为空（所有字段为null或字符串字段为空字符串）
	 *
	 * @param o            源对象
	 * @param ignoreFields 忽略字段
	 * @return true|false
	 */
	public static boolean beanIsEmpty(Object o, String... ignoreFields) {
		if (o == null) {
			return true;
		}
		if (StrUtil.isBlankIfStr(o)) {
			return true;
		}
		Field[] fields = ReflectUtil.getFields(o.getClass());
		for (Field field : fields) {
			if (ignoreFields != null && ArrayUtil.contains(ignoreFields, field.getName())) {
				continue;
			}
			Object value = ReflectUtil.getFieldValue(o, field);
			if (value != null && !StrUtil.isBlankIfStr(o)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 美化显示BigDecimal数字
	 *
	 * @param src 原始数字
	 * @return 调用BigDecimal.toPlainString()，并去除尾小数点儿后多余的0
	 */
	public static BigDecimal prettyBigDecimal(BigDecimal src) {
		return new BigDecimal(NumberUtil.toStr(src));
	}

	/**
	 * 美化显示BigDecimal数字
	 *
	 * @param src   原始数字
	 * @param scale 四舍五入保留小数位
	 * @return 调用BigDecimal.toPlainString()，并去除尾小数点儿后多余的0
	 */
	public static BigDecimal prettyBigDecimal(BigDecimal src, int scale) {
		return new BigDecimal(NumberUtil.toStr(src.setScale(scale, RoundingMode.HALF_UP)));
	}

	/**
	 * id转为base62
	 *
	 * @param id 数据id
	 * @return base62编码
	 */
	public static String idToBase62(Long id) {
		if (id == null) {
			return null;
		}
		byte[] unsignedByteArray = NumberUtil.toUnsignedByteArray(BigInteger.valueOf(id));
		return Base62.encode(unsignedByteArray);
	}

	/**
	 * base62转为id
	 *
	 * @param base62 base62编码
	 * @return id
	 */
	public static Long base62ToId(String base62) {
		if (StrUtil.isBlank(base62)) {
			return null;
		}
		return SafeUtil.call(() -> {
			byte[] decode = Base62.decode(base62.trim());
			return NumberUtil.fromUnsignedByteArray(decode).longValue();
		});
	}

	public static String getTraceId() {
		return MDC.get("traceId");
	}

}
